package pipe

import (
	"context"
	"fmt"
	"gitee.com/injoyai/goutil/maps"
	"gitee.com/injoyai/goutil/net/conn/v2"
	"log"
	"net"
	"strconv"
	"strings"
	"time"
)

func NewServerTCP(port int) (*ServerTCP, error) {
	return NewServerTCPWhitContext(context.Background(), port)
}

func NewServerTCPWhitContext(ctx context.Context, port int) (*ServerTCP, error) {
	pipeServer, err := NewServerWithContext(ctx, port)
	if err != nil {
		return nil, err
	}
	ctx, cancel := context.WithCancel(ctx)
	s := &ServerTCP{
		Pipe:    pipeServer,
		mListen: maps.NewSafe(),
		mIpSn:   maps.NewSafe(),
		ctx:     ctx,
		cancel:  cancel,
	}
	return s, nil
}

type Conn struct {
	hasData bool
	net.Conn
}

func (this *Conn) Write(p []byte) (int, error) {
	if !this.hasData {
		this.hasData = !strings.Contains(string(p), Connection)
	}
	return this.Conn.Write(p)
}

func (this *Conn) Close() error {
	if !this.hasData {
		return this.Conn.Close()
	}
	// 设置SetDeadline能让数据传输完成再关闭连接
	// 用close会出现数据未传输完成就关掉连接的情况
	return this.SetWriteDeadline(time.Time{}) //.SetDeadline(time.Time{})
}

type ServerTCP struct {
	Pipe    *Server
	mListen *maps.Safe //映射关系
	mIpSn   *maps.Safe
	ctx     context.Context
	cancel  context.CancelFunc
}

// SetDealWriteFunc 设置写入通道的数据,处理请求的数据,例如修改请求头,过滤非法请求等...
func (this *ServerTCP) SetDealWriteFunc(fn func(r *Request, listenPort int, data []byte) ([]byte, error)) *ServerTCP {
	this.Pipe.SetDealWriteFunc(fn)
	return this
}

// SetDealReadFunc 设置读取通道的数据,处理读取到的数据
// 默认使用修改SN码和修改请求地址函数
func (this *ServerTCP) SetDealReadFunc(fn func(c *conn.Conn, msg *Message) error) *ServerTCP {
	this.Pipe.SetDealReadFunc(fn)
	return this
}

// Debug 调试模式
func (this *ServerTCP) Debug(b ...bool) *ServerTCP {
	this.PipeDebug(b...)
	this.ProxyDebug(b...)
	return this
}

// PipeDebug 调试模式
func (this *ServerTCP) PipeDebug(b ...bool) *ServerTCP {
	this.Pipe.Server.Debug(b...)
	return this
}

// ProxyDebug 调试模式
func (this *ServerTCP) ProxyDebug(b ...bool) *ServerTCP {
	this.Pipe.Debug(b...)
	return this
}

// Run 开始运行
func (this *ServerTCP) Run() error {
	return this.Pipe.Run()
}

// RangeListen 遍历监听服务
func (this *ServerTCP) RangeListen(fn func(port int, listen *Listen) bool) *ServerTCP {
	this.mListen.Range(func(key, value interface{}) bool {
		return fn(key.(int), value.(*Listen))
	})
	return this
}

// GetListen 获取Listen
func (this *ServerTCP) GetListen(port int) *Listen {
	listen, _ := this.mListen.Get(port)
	if listen != nil {
		return listen.(*Listen)
	}
	return nil
}

// Listen 监听端口,映射的地址
// @pipeSn 默认通道
// @proxyAddr 默认代理地址
func (this *ServerTCP) Listen(port int, pipeSn, proxyAddr string) error {
	if port == this.Pipe.Server.GetPort() {
		return ErrPortBind
	}
	listen, err := this.newListen(port, pipeSn, proxyAddr)
	if err != nil {
		return err
	}
	if _, has := this.mListen.GetOrSet(listen.getPort(), listen); has {
		return ErrPortBind
	}
	go func(ctx context.Context, listen *Listen) {
		for {
			c, err := listen.Accept()
			if err != nil {
				return
			}
			// listener关闭,已连接的conn不会关闭
			r := newRequest(ctx, listen.getSn(), c.RemoteAddr().String(), listen.getAddr(), &Conn{Conn: c})
			this.Pipe.SetClient(r, listen.getPort())
		}
	}(this.ctx, listen)
	log.Printf("开启TCP服务(:%d)成功......\n", port)
	return nil
}

// SetListenSN 设置连接的通道sn
func (this *ServerTCP) SetListenSN(port int, sn string) *ServerTCP {
	listen := this.mListen.GetVar(strconv.Itoa(port))
	if !listen.IsNil() {
		listen.Val().(*Listen).setSn(sn)
	}
	return this
}

// SetListenAddr 设置请求的默认地址,可被客户端重定向
func (this *ServerTCP) SetListenAddr(port int, addr string) *ServerTCP {
	listen := this.mListen.GetVar(strconv.Itoa(port))
	if !listen.IsNil() {
		listen.Val().(*Listen).setAddr(addr)
	}
	return this
}

// newListen 新建监听服务
func (this *ServerTCP) newListen(port int, sn, addr string) (*Listen, error) {
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	listen := &Listen{
		port:     port,
		sn:       sn,
		addr:     addr,
		Listener: listener,
	}
	listen.setAddr(addr)
	return listen, nil
}

//// WriteWithDefault 默认函数,通过请求数据修改SN和请求地址
//func (this *ServerTCP) WriteWithDefault(r *Request, listenPort int, data []byte) ([]byte, error) {
//	{
//		// getAddr 可以更换请求地址,留了后门(能代理所有,客户端配合的情况下)
//		addr := regexp.MustCompile(`(\?|&|#)(_addr=).*?&`).FindString(string(data))
//		if len(addr) > 6 {
//			addr = addr[7 : len(addr)-1]
//			r.SetAddr(addr)
//			listen := this.mListen.Get(listenPort)
//			if !listen.IsNil() {
//				listen.Val().(*Listen).setAddr(addr)
//			}
//		}
//	}
//	{
//		// 可以修改sn
//		sn := regexp.MustCompile(`(\?|&|#)(_sn=).*?&`).FindString(string(data))
//		ip := str.CropLast(r.Key(), ":", false)
//		if len(sn) > 4 {
//			sn = sn[5 : len(sn)-1]
//			this.mIpSn.Set(ip, sn)
//			log.Printf("[Bind][%s] %s", r.Key(), sn)
//		} else {
//			sn = this.mIpSn.GetString(ip, r.SN)
//		}
//		r.SetSN(sn)
//		listen := this.mListen.Get(listenPort)
//		if !listen.IsNil() {
//			listen.Val().(*Listen).setSn(sn)
//		}
//	}
//	return data, nil
//}
